package xyz.zyl2020.securityjwt.config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.userdetails.User;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.security.web.authentication.AuthenticationSuccessHandler;
import org.springframework.util.CollectionUtils;
import xyz.zyl2020.securityjwt.dao.UserDao;
import xyz.zyl2020.securityjwt.entity.Role;
import xyz.zyl2020.securityjwt.filter.JwtAuthorizationFilter;
import xyz.zyl2020.securityjwt.utils.JwtUtil;

import javax.annotation.Resource;
import javax.servlet.http.Cookie;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;

import static xyz.zyl2020.securityjwt.utils.JwtUtil.TOKEN_HEADER;

/**
 * Security配置类
 *
 * @author ZhuYinglong
 * @date 2020/8/19 0019
 */
@EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    private final Logger log = LoggerFactory.getLogger(this.getClass());

    @Resource
    private UserDao userDao;

    /**
     * 配置请求授权规则
     *
     * @param http
     * @throws Exception
     */
    @Override
    protected void configure(HttpSecurity http) throws Exception {
        http.authorizeRequests()
                .antMatchers("/css/**", "/js/**", "/", "/index", "/loginPage").permitAll() // 无需认证
                .anyRequest().authenticated() // 其他请求都需要认证
        ;

        http.addFilter(new JwtAuthorizationFilter(authenticationManager()));

        http.formLogin() // 开启登录，如果没有权限，就会跳转到登录页
                .loginPage("/loginPage") // 自定义登录页，默认/login（get请求）
                .loginProcessingUrl("/login") // 登录处理地址，默认/login（post请求）
                .usernameParameter("inputEmail") // 自定义username属性名，默认username
                .passwordParameter("inputPassword") // 自定义password属性名，默认password
                .successHandler(loginSuccessHandler())
        ;

        http.rememberMe() // 开启记住我
                .rememberMeParameter("rememberMe") // 自定义rememberMe属性名
        ;

        http.logout() // 开启注销
                .logoutUrl("/logout") // 注销处理路径，默认/logout
                .logoutSuccessUrl("/") // 注销成功后跳转路径
                .deleteCookies(TOKEN_HEADER) // 删除cookie
        ;

        http.csrf().disable(); // 禁止csrf

        http.sessionManagement() // session管理
                .sessionCreationPolicy(SessionCreationPolicy.STATELESS); // 无状态，即不创建Session，也不使用SecurityContext获取Session
    }

    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.userDetailsService(userDetailsService())
                .passwordEncoder(passwordEncoder());
    }

    /**
     * 鉴权
     *
     * @return
     */
    @Bean
    public UserDetailsService userDetailsService() {
        return username -> {
            xyz.zyl2020.securityjwt.entity.User user = userDao.findByUsernameOrEmail(username, username);
            if (user == null) {
                throw new UsernameNotFoundException("账号或密码错误！");
            }
            String[] roleCodeArray = user.getRoles().stream().map(Role::getCode).toArray(String[]::new);

            return User.withUsername(user.getUsername())
                    .password(user.getPassword())
                    .authorities(roleCodeArray)
                    .build();
        };
    }

    /**
     * 配置密码编码格式
     *
     * @return
     */
    @Bean
    public PasswordEncoder passwordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 登录成功处理
     *
     * @return
     */
    @Bean
    public AuthenticationSuccessHandler loginSuccessHandler() {
        return (request, response, authentication) -> {
            List<String> authorities = new ArrayList<>();
            if (!CollectionUtils.isEmpty(authentication.getAuthorities())) {
                authorities.addAll(authentication.getAuthorities().stream().map(GrantedAuthority::getAuthority).collect(Collectors.toList()));
            }
            String token = JwtUtil.generateToken(authentication.getName(), authorities);
            // 将token添加到header中
            response.setHeader(TOKEN_HEADER, JwtUtil.TOKEN_PREFIX + token);
            // 将token添加到cookie中
            Cookie cookie = new Cookie(TOKEN_HEADER, JwtUtil.TOKEN_PREFIX + token);
            cookie.setPath("/");
            cookie.setMaxAge(JwtUtil.TTL_COOKIE);
            response.addCookie(cookie);
            log.info("登录成功，username: {}, token: {}", authentication.getName(), token);
        };
    }
}
